#include "DbDataManager.h"
#include <cstddef>
#include "Logger.h"

static constexpr char DB_NAME[] = "../data/chat_user.db3";

static constexpr char USER_TALBE_NAME[] = "user";

static constexpr char FRIENDSHIP_TABLE_NAME[] = "friendship";

static constexpr char USER_TABLE_CREATE_SQL[] = "CREATE TABLE IF NOT EXISTS user("
                                                "id INTEGER PRIMARY KEY AUTOINCREMENT,"
                                                "name TEXT NOT NULL,"
                                                "password TEXT NOT NULL,"
                                                "status TEXT NOT NULL,"
                                                "create_time INTEGER NOT NULL,"
                                                "last_login_time INTEGER NOT NULL,"
                                                "last_logout_time INTEGER NOT NULL,"
                                                "UNIQUE(name))";

static constexpr char FRIENDSHIP_TABLE_CREATE_SQL[] =
    "CREATE TABLE IF NOT EXISTS friendship("
    "UserID INTEGER NOT NULL,"
    "FriendID INTEGER NOT NULL,"
    "PRIMARY KEY (UserID, FriendID),"
    "FOREIGN KEY (UserID) REFERENCES Users(UserID) ON DELETE CASCADE,"
    "FOREIGN KEY (FriendID) REFERENCES Users(UserID) ON DELETE CASCADE"
    ")";
DbDataManager::DbDataManager() : user_db_(DB_NAME, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE)
{
    LOG_INFO(CONSOLE_LOG) << "db filename "_s << user_db_.getFilename() << " open successful";

    if (user_db_.tableExists(USER_TALBE_NAME)) {
        LOG_INFO(CONSOLE_LOG) << "table "_s << USER_TALBE_NAME << " exists";
    }
    else {
        user_db_.exec(USER_TABLE_CREATE_SQL);
        if (user_db_.tableExists(USER_TALBE_NAME)) {
            LOG_INFO(CONSOLE_LOG) << "table "_s << USER_TALBE_NAME << " create successful";
        }
        else {
            LOG_ERROR(CONSOLE_LOG) << "table "_s << USER_TALBE_NAME << " create failed " << user_db_.getErrorMsg();
        }
    }
    if (user_db_.tableExists(FRIENDSHIP_TABLE_NAME)) {
        LOG_INFO(CONSOLE_LOG) << "table "_s << FRIENDSHIP_TABLE_NAME << " exists";
    }
    else {
        user_db_.exec(FRIENDSHIP_TABLE_CREATE_SQL);
        if (user_db_.tableExists(FRIENDSHIP_TABLE_NAME)) {
            LOG_INFO(CONSOLE_LOG) << "table "_s << FRIENDSHIP_TABLE_NAME << " create successful";
        }
        else {
            LOG_ERROR(CONSOLE_LOG) << "table "_s << FRIENDSHIP_TABLE_NAME << " create failed "
                                   << user_db_.getErrorMsg();
        }
    }
}

DbDataManager::~DbDataManager()
{
    LOG_INFO(CONSOLE_LOG) << "db filename "_s << user_db_.getFilename() << " close successful";
}

user_id_t DbDataManager::addUser(const User& user)
{
    user_id_t           user_id = -1;
    SQLite::Transaction transaction(user_db_);
    SQLite::Statement   insert(user_db_, "INSERT INTO user(name, password, status, create_time, last_login_time, "
                                           "last_logout_time) VALUES(?, ?, ?, ?, ?, ?)");
    insert.bind(1, user.name);
    insert.bind(2, user.password);
    insert.bind(3, user.isOnline ? "online" : "offline");
    insert.bind(4, 0);
    insert.bind(5, 0);
    insert.bind(6, 0);

    if (insert.exec() == 1) {
        LOG_INFO(CONSOLE_LOG) << "insert user "_s << user.name << " success";
        user_id = user_db_.getLastInsertRowid();
    }
    else {
        LOG_ERROR(CONSOLE_LOG) << "insert user "_s << user.name << " failed " << insert.getErrorMsg();
        return user_id;
    }
    size_t friend_insert_count = 0;
    if (user.friends.size() > 0) {
        for (auto& friend_id : user.friends) {
            SQLite::Statement insert2(user_db_, "INSERT INTO friendship(UserID, FriendID) VALUES(?, ?)");
            insert2.bind(1, user.id);
            insert2.bind(2, friend_id);
            if (insert2.exec() == 1) {
                LOG_INFO(CONSOLE_LOG) << "insert user "_s << user.name << " friend "_s << friend_id << " success";
                friend_insert_count++;
            }
            else {
                LOG_ERROR(CONSOLE_LOG) << "insert user "_s << user.name << " friend "_s << friend_id << " failed "
                                       << insert2.getErrorMsg();
                return user_id;
            }
        }
    }

    if (friend_insert_count == user.friends.size()) {
        transaction.commit();
    }
    else {
        LOG_ERROR(CONSOLE_LOG) << "insert user "_s << user.name << " friend failed rollback ";
    }

    return user_id;
}
bool DbDataManager::getUser(const user_id_t id, User& user)
{
    SQLite::Statement select(user_db_, "SELECT * FROM user WHERE id = ?");
    select.bind(1, id);
    if (select.executeStep()) {
        user.id       = select.getColumn(0).getInt64();
        user.name     = select.getColumn(1).getString();
        user.password = select.getColumn(2).getString();
        user.isOnline = select.getColumn(3).getString() == "online" ? true : false;
        SQLite::Statement select2(user_db_, "SELECT FriendID FROM friendship WHERE UserID = ?");
        select2.bind(1, id);
        while (select2.executeStep()) {
            user.friends.push_back(select2.getColumn(0).getInt64());
        }
        LOG_INFO(CONSOLE_LOG) << "get user "_s << id << " success";
    }
    else {
        LOG_ERROR(CONSOLE_LOG) << "get user "_s << id << " failed " << select.getErrorMsg();
        return false;
    }

    return true;
}
bool DbDataManager::updateUser(const User& user)
{
    const auto          id = user.id;
    SQLite::Transaction transaction(user_db_);
    SQLite::Statement   update(user_db_, "UPDATE user SET name = ?, password = ?, status = ? WHERE id = ?");
    update.bind(1, user.name);
    update.bind(2, user.password);
    update.bind(3, user.isOnline ? "online" : "offline");

    if (update.exec() == 1) {
        LOG_INFO(CONSOLE_LOG) << "update user "_s << id << " success";
        if (user.friends.size() > 0) {
            SQLite::Statement delete2(user_db_, "DELETE FROM friendship WHERE UserID = ?");
            delete2.bind(1, id);
            if (delete2.exec() >= 1) {
                LOG_INFO(CONSOLE_LOG) << "delete user "_s << id << " friends success";
            }
            else {
                LOG_ERROR(CONSOLE_LOG) << "delete user "_s << id << " friends failed " << delete2.getErrorMsg();
                return false;
            }
            for (auto& friend_id : user.friends) {
                SQLite::Statement insert2(user_db_, "INSERT INTO friendship(UserID, FriendID) VALUES(?, ?)");
                insert2.bind(1, id);
                insert2.bind(2, friend_id);
                if (insert2.exec() == 1) {
                    LOG_INFO(CONSOLE_LOG) << "insert user"_s << id << " friend "_s << friend_id << " success";
                }
                else {
                    LOG_ERROR(CONSOLE_LOG)
                        << "insert user"_s << id << " friend "_s << friend_id << " failed " << insert2.getErrorMsg();
                    return false;
                }
            }
        }
    }
    else {
        LOG_ERROR(CONSOLE_LOG) << "update user "_s << user.id << " failed";
        return false;
    }

    transaction.commit();

    return true;
}

bool DbDataManager::updateFriendShip(const User& user, const User& otherUser)
{
    SQLite::Transaction transaction(user_db_);
    bool                ret = updateOneUserFriendship(user);
    if (!ret) {
        LOG_ERROR(CONSOLE_LOG) << "update user"_s << user.id << " friend "_s << otherUser.id << " failed";
        return false;
    }
    ret = updateOneUserFriendship(otherUser);
    if (!ret) {
        LOG_ERROR(CONSOLE_LOG) << "update user"_s << otherUser.id << " friend "_s << user.id << " failed";
        return false;
    }
    transaction.commit();
    return true;
}

bool DbDataManager::addNewFriend(user_id_t userId, user_id_t otherUserId)
{
    SQLite::Transaction transaction(user_db_);
    SQLite::Statement   insert(user_db_, "INSERT INTO friendship(UserID, FriendID) VALUES(?, ?)");
    insert.bind(1, userId);
    insert.bind(2, otherUserId);
    if (insert.exec() == 1) {
        LOG_INFO(CONSOLE_LOG) << "insert user "_s << userId << " friend "_s << otherUserId << " success";
    }
    else {
        LOG_ERROR(CONSOLE_LOG) << "insert user "_s << userId << " friend "_s << otherUserId << " failed "
                               << insert.getErrorMsg();
        return false;
    }

    SQLite::Statement insert2(user_db_, "INSERT INTO friendship(UserID, FriendID) VALUES(?, ?)");
    insert2.bind(1, otherUserId);
    insert2.bind(2, userId);
    if (insert2.exec() == 1) {
        LOG_INFO(CONSOLE_LOG) << "insert user "_s << otherUserId << " friend "_s << userId << " success";
    }
    else {
        LOG_ERROR(CONSOLE_LOG) << "insert user "_s << otherUserId << " friend "_s << userId << " failed "
                               << insert2.getErrorMsg();
        return false;
    }

    transaction.commit();

    return true;
}

bool DbDataManager::deleteFriend(user_id_t userId, user_id_t otherUserId)
{
    SQLite::Transaction transaction(user_db_);
    SQLite::Statement   delete2(user_db_, "DELETE FROM friendship WHERE UserID = ? AND FriendID = ?");
    delete2.bind(1, userId);
    delete2.bind(2, otherUserId);
    if (delete2.exec() >= 1) {
        LOG_INFO(CONSOLE_LOG) << "delete user"_s << userId << "friend "_s << otherUserId << " success";
    }
    else {
        LOG_ERROR(CONSOLE_LOG) << "delete user"_s << userId << "friend "_s << otherUserId << " failed "
                               << delete2.getErrorMsg();
        return false;
    }
    SQLite::Statement delete3(user_db_, "DELETE FROM friendship WHERE UserID = ? AND FriendID = ?");
    delete3.bind(1, otherUserId);
    delete3.bind(2, userId);
    if (delete3.exec() >= 1) {
        LOG_INFO(CONSOLE_LOG) << "delete user"_s << otherUserId << "friend "_s << userId << " success";
    }
    else {
        LOG_ERROR(CONSOLE_LOG) << "delete user"_s << otherUserId << "friend "_s << userId << " failed "
                               << delete3.getErrorMsg();
        return false;
    }
    transaction.commit();
    return true;
}

bool DbDataManager::updateOneUserFriendship(const User& user)
{
    if (user.friends.size() > 0) {
        // 存在好友关系才进行删除 先统计好友数量
        SQLite::Statement select2(user_db_, "SELECT COUNT(*) FROM friendship WHERE UserID = ?");
        select2.bind(1, user.id);
        if (select2.executeStep()) {
            const int count = select2.getColumn(0).getInt();
            if (count > 0) {
                SQLite::Statement delete2(user_db_, "DELETE FROM friendship WHERE UserID = ?");
                delete2.bind(1, user.id);
                if (delete2.exec() >= 1) {
                    LOG_INFO(CONSOLE_LOG) << "delete user:"_s << user.id << " friends success";
                }
                else {
                    LOG_ERROR(CONSOLE_LOG)
                        << "delete user:"_s << user.id << " friends failed " << delete2.getErrorMsg();
                    return false;
                }
            }
        }

        for (auto& friend_id : user.friends) {
            SQLite::Statement insert2(user_db_, "INSERT INTO friendship(UserID, FriendID) VALUES(?, ?)");
            insert2.bind(1, user.id);
            insert2.bind(2, friend_id);
            if (insert2.exec() == 1) {
                LOG_INFO(CONSOLE_LOG) << "insert user:"_s << user.id << " friend "_s << friend_id << " success";
            }
            else {
                LOG_ERROR(CONSOLE_LOG) << "insert user:"_s << user.id << " friend "_s << friend_id << " failed "
                                       << insert2.getErrorMsg();
                return false;
            }
        }
    }
    return true;
}

bool DbDataManager::deleteUser(const user_id_t id)
{
    SQLite::Transaction transaction(user_db_);
    SQLite::Statement   delete1(user_db_, "DELETE FROM user WHERE id = ?");
    delete1.bind(1, id);
    if (delete1.exec() == 1) {
        LOG_INFO(CONSOLE_LOG) << "delete user "_s << id << " success";
        SQLite::Statement delete2(user_db_, "DELETE FROM friendship WHERE UserID = ?");
        delete2.bind(1, id);
        if (delete2.exec() == 1) {
            LOG_INFO(CONSOLE_LOG) << "delete user "_s << id << " friends success";
        }
        else {
            LOG_ERROR(CONSOLE_LOG) << "delete user "_s << id << " friends failed " << delete2.getErrorMsg();
            return false;
        }
    }
    else {
        LOG_ERROR(CONSOLE_LOG) << "delete user "_s << id << " failed";
        return false;
    }

    transaction.commit();
    return true;
}

std::set<std::string> DbDataManager::getAllUserNames()
{
    std::set<std::string> names;
    SQLite::Statement     select(user_db_, "SELECT name FROM user");
    while (select.executeStep()) {
        names.insert(select.getColumn(0).getString());
    }
    return names;
}

std::unordered_map<user_id_t, User> DbDataManager::getAllUsers()
{
    std::unordered_map<user_id_t, User> users;
    SQLite::Statement                   select(user_db_, "SELECT * FROM user");
    while (select.executeStep()) {
        User user     = {};
        user.id       = select.getColumn(0).getInt64();
        user.name     = select.getColumn(1).getString();
        user.password = select.getColumn(2).getString();
        user.isOnline = false;
        SQLite::Statement select2(user_db_, "SELECT FriendID FROM friendship WHERE UserID = ?");
        select2.bind(1, user.id);
        while (select2.executeStep()) {
            user.friends.push_back(select2.getColumn(0).getInt64());
        }
        users.insert(std::make_pair(user.id, user));
        LOG_INFO(CONSOLE_LOG) << "get user "_s << user.id << " success";
    }
    return users;
}
